package lionel.MyJMRI.MyTrain;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Vector;

import javax.swing.Timer;

import jmri.DccLocoAddress;
import jmri.DccThrottle;
import jmri.LocoAddress;
import jmri.ThrottleListener;
import lionel.MyBrowser.MyConductor;


public class MyTrainConductor implements MyConductor, ThrottleListener {
	static org.apache.log4j.Category log = org.apache.log4j.Category.getInstance(MyTrain.class.getName());

	static final float CRUISE_SPEED = 0.6f ;
	static final float SLOW_SPEED = 0.2f ;
	static final float ESTOP_SPEED = -0.5f ;
	static final float STOP_SPEED = 0f ;
	static final float MAX_SPEED = 1f ;
	
	private static final int SPEED_SET_INTERVAL = 50 ;
	private static final int THROTTLE_CHECK_INTERVAL = 500;
	
	private DccThrottle m_throttle = null; 

	private Timer speedSetTimer = null;
	private SpeedSetTask speedSetTask = null;

	private Timer checkThrottleTimer = null;
	private CheckThrottleTask checkThrottleTask = null;
	private int checkThrottleTic = 0;

	protected LocoAddress mLocoAddress = null;	
	private boolean mOnListen = false;
	private boolean mOnError = false;
	
    // data members to hold contact with the property listeners
    final private Vector listeners = new Vector();
    
    public MyTrainConductor(LocoAddress locoAddress) {
		speedSetTask = new SpeedSetTask();
		speedSetTimer = new Timer( SPEED_SET_INTERVAL, speedSetTask );
		checkThrottleTask = new CheckThrottleTask();
		checkThrottleTimer = new Timer( THROTTLE_CHECK_INTERVAL, checkThrottleTask);
		m_throttle = null;
    	mLocoAddress = locoAddress;
    }

    public String toString()
    {
    	if (mLocoAddress != null)
    		return mLocoAddress.toString() ;
    	else
    		return super.toString();
    }
    
	// register for notification if any of the properties change
    public void removePropertyChangeListener(PropertyChangeListener l) {
        if (listeners.contains(l)) {
            listeners.removeElement(l);
        }
    }

    public void addPropertyChangeListener(PropertyChangeListener l) {
        // add only if not already registered
        if (!listeners.contains(l)) {
            listeners.addElement(l);
        }
    }

    /**
     * Trigger the notification of all PropertyChangeListeners
     */
    protected void notifyPropertyChangeListener(String property, Object oldValue, Object newValue) {
        // make a copy of the listener vector to synchronized not needed for transmit
        Vector v;
        synchronized(this)
            {
                v = (Vector) listeners.clone();
            }
        if (log.isDebugEnabled()) log.debug("notify "+v.size()
                                            +" listeners about property "
                                            +property);
        // forward to all listeners
        int cnt = v.size();
        for (int i=0; i < cnt; i++) {
            PropertyChangeListener client = (PropertyChangeListener) v.elementAt(i);
            client.propertyChange(new PropertyChangeEvent(this, property, oldValue, newValue));
        }
    }
	
	private class SpeedSetTask implements ActionListener {
		static final float SPEED_INCREMENT = 0.01f ;
		public int WkDir;

		public void actionPerformed(ActionEvent e) {
			float sp = m_throttle.getSpeedSetting() + WkDir * SPEED_INCREMENT;
			if (sp<0) sp = 0;
			if (sp>1) sp = 1;
			m_throttle.setSpeedSetting( sp );
			notifyPropertyChangeListener("SpeedSetting", sp, sp);
		}
	}

	private class CheckThrottleTask implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			checkThrottleTic++;
			if (checkThrottle())
				checkThrottleTimer.stop();
		}
	}

	public synchronized void locAccStop() {
		log.debug("Stopping acceleration task");
		if (speedSetTimer!=null)
			if (speedSetTimer.isRunning())
				speedSetTimer.stop();
	} 

	public synchronized void locAcc(boolean yn) {
		log.debug("Starting acceleration task");
		speedSetTask.WkDir = yn?1:-1;
		if ( checkThrottle() && (!speedSetTimer.isRunning()) )
			speedSetTimer.start();  		
	}

	public synchronized void locSwitchDir() {
		log.debug("locSwitchDir");
		if ( checkThrottle() ) { locDir( ! m_throttle.getIsForward() ); }
	}

	public synchronized void locDir(boolean plus) {
		locAccStop();
		if ( checkThrottle() ) { m_throttle.setIsForward( plus ); notifyPropertyChangeListener("IsForward", !plus, plus); } 
	}

	public synchronized void locSwitchLight() {
		if ( checkThrottle() ) { locLight(! m_throttle.getF0() ) ; } 
	}

	public synchronized void locLight(boolean on) {
		if ( checkThrottle() ) { m_throttle.setF0(on); notifyPropertyChangeListener("F0", on, on);} 
	}

	public synchronized void locEStop() {
		locAccStop();
		if ( checkThrottle() ) { m_throttle.setSpeedSetting(ESTOP_SPEED); notifyPropertyChangeListener("SpeedSetting", ESTOP_SPEED, ESTOP_SPEED);}
	}

	public synchronized void locSetSpeed(float speed) {
		locAccStop();
		if ( checkThrottle() ) { m_throttle.setSpeedSetting( speed ); notifyPropertyChangeListener("SpeedSetting", speed, speed);}
	}

	public synchronized float locGetSpeed() {
		float speed = 0;
		if ( checkThrottle() ) { speed = m_throttle.getSpeedSetting(); }
		return speed;
	}

	public synchronized void locStop() {
		log.debug("locStop()");
		locAccStop();
		if ( checkThrottle() ) { m_throttle.setSpeedSetting(STOP_SPEED); notifyPropertyChangeListener( "SpeedSetting", STOP_SPEED, STOP_SPEED);}
	}

	public synchronized void locSlow() {
		log.debug("locSlow()");
		locAccStop();
		if ( checkThrottle() ) { m_throttle.setSpeedSetting(SLOW_SPEED);  notifyPropertyChangeListener("SpeedSetting", SLOW_SPEED, SLOW_SPEED);}
	}  

	public synchronized void locCruise() {
		log.debug("locCruise()");
		locAccStop();
		if ( checkThrottle() ) { m_throttle.setSpeedSetting(CRUISE_SPEED); notifyPropertyChangeListener( "SpeedSetting", CRUISE_SPEED, CRUISE_SPEED);}
	}

	public synchronized void locMax() {
		log.debug("locMax()");
		locAccStop();
		if ( checkThrottle() ) { m_throttle.setSpeedSetting(MAX_SPEED);  notifyPropertyChangeListener("SpeedSetting", MAX_SPEED, MAX_SPEED);}
	}
	
	public synchronized void locSwitchF1() {
		locF1(! m_throttle.getF1());
	}
	public synchronized void locF1(boolean on) {
		log.debug("locF1() to "+on);
		locAccStop();
		if ( checkThrottle() ) { m_throttle.setF1(on); notifyPropertyChangeListener("F1", !on, on);}
	}
	public synchronized void locSwitchF2() {
		locF2(! m_throttle.getF2());
	}
	public synchronized void locF2(boolean on) {
		log.debug("locF2() to "+on);
		locAccStop();
		if ( checkThrottle() ) { m_throttle.setF2(on); notifyPropertyChangeListener("F2", !on, on);}
	}
	public synchronized void locF3(boolean on) {
		log.debug("locF3() to "+on);
		locAccStop();
		if ( checkThrottle() ) { m_throttle.setF3(on); notifyPropertyChangeListener("F3", !on, on);}
	}
	public synchronized void locF4(boolean on) {
		log.debug("locF4() to "+on);
		locAccStop();
		if ( checkThrottle() ) { m_throttle.setF4(on); notifyPropertyChangeListener("F4", !on, on);}
	}
	public synchronized void locF5(boolean on) {
		log.debug("locF5() to "+on);
		locAccStop();
		if ( checkThrottle() ) { m_throttle.setF5(on); notifyPropertyChangeListener("F5", !on, on);}
	}
	public synchronized void locF6(boolean on) {
		log.debug("locF6() to "+on);
		locAccStop();
		if ( checkThrottle() ) { m_throttle.setF6(on); notifyPropertyChangeListener("F6", !on, on);}
	}

	private synchronized boolean checkThrottle()
	{
		if (m_throttle != null)
			return true;

		if (checkThrottleTic == 2)
		{
			mOnError = true;
			notifyPropertyChangeListener("Image", "Default", (getClass().getResource("NoThrottle.jpg")).getFile() );
		}
		return false;
	}

	public synchronized boolean requestThrottle()
	{
		if (mOnListen == false)
		{
			log.debug("Requesting throttle for "+mLocoAddress);
			if ( jmri.InstanceManager.throttleManagerInstance().requestThrottle( ((DccLocoAddress)mLocoAddress).getNumber(), this) == false )
			{
				notifyPropertyChangeListener("Image", "Default", (getClass().getResource("NoThrottle.jpg")).getFile() );
				log.error("Throttle request refused by system");
				return false;
			}
			checkThrottleTic = 0;
			checkThrottleTimer.start();
			mOnListen = true;
		}
		return true;
	}

	public synchronized void releaseThrottle()
	{
		locAccStop();
		locStop();
		locLight(false);
		mOnError = false;
		if (checkThrottleTimer.isRunning())
		{
			log.debug("Canceling throttle request for "+mLocoAddress);
			jmri.InstanceManager.throttleManagerInstance().cancelThrottleRequest( ((DccLocoAddress)mLocoAddress).getNumber(), this);
			checkThrottleTimer.stop();
		}	
		if (m_throttle != null)
		{	
			log.debug("Releasing throttle for "+mLocoAddress);
			m_throttle.release();
		}
		notifyPropertyChangeListener("Throttle", m_throttle, null);
		m_throttle = null;
	}

	public synchronized void notifyThrottleFound(DccThrottle t) {
		log.debug("Got throttle "+t+" for "+mLocoAddress);
		m_throttle = t;
		checkThrottleTic = 0;
		if (checkThrottleTimer.isRunning() )
			checkThrottleTimer.stop();
		notifyPropertyChangeListener("Throttle", null, m_throttle);	
		mOnListen = false;
		locLight(true);
		locDir(true);
		if (mOnError)
		{
			mOnError = false;
			notifyPropertyChangeListener("Image", null, "Default" );
		}
	}

}
